<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>EventEmitter源码学习</title>
</head>
<script>
    (function (exports) {
        function EventEmitter() { };

        var proto = EventEmitter.prototype;
        var originalGlobalValue = exports.EventEmitter;

        function indexOfListener(listeners, listener) {
            var i = listeners.length;
            while (i--) {
                if (listeners[i].listener === listener) {
                    return i;
                }
            }
            return -1;
        }

        function alias(name) {
            return function () {
                return this[name].apply(this, arguments);
            }
        }

        proto.getListeners = function getListeners(evt) {
            var events = this._getEvents();
            var response;
            var key;

            if (evt instanceof RegExp) {
                response = {};
                for (key in events) {
                    if (events.hasOwnProperty(key) && evt.test(key)) {
                        response[key] = events[key];
                    }
                }
            }
            else {
                response = events[evt] || (events[evt] = []);
            }

            return response;
        }

        proto.flattenListeners = function flattenListeners(listeners) {
            var flatListeners = [];
            var i;
            for (i = 0; i < listeners.length; ++i) {
                flatListeners.push(listeners[i].listener);
            }
            return flatListeners;
        }

        proto.getListenersAsObject = function (evt) {
            var listeners = this.getListeners(evt);
            var response;

            if (listeners instanceof Array) {
                response = {};
                response[evt] = listeners;
            }

            return response || listeners;
        }

        function isValidListener(listener) {
            if (typeof listener === 'function' || listener instanceof RegExp) {
                return true;
            } else if (listener && typeof listener == 'object') {
                return isValidListener(listener.listener);
            } else {
                return false;
            }
        }

        proto.addListener = function (evt, listener) {
            if (!isValidListener(listener)) {
                throw new TypeError('listener must be a function')
            }

            var listeners = this.getListenersAsObject(evt);
            var listenerIsWrapped = typeof listener === 'object';
            var key;

            for (key in listeners) {
                if (listeners.hasOwnProperty(key) && indexOfListener(listeners, listener) === -1) {
                    listeners[key].push(listenerIsWrapped ? listener : {
                        listener: listener,
                        once: false
                    })
                }
            }

            return this;
        }

        proto.on = proto.addListener;

        proto.addOnceListener = function (evt, listener) {
            return this.addListener(evt, {
                listener: listener,
                once: true
            });
        };

        proto.once = proto.addOnceListener;

        proto.defineEvent = function defineEvent(evt) {
            this.getListeners(evt);
            return this;
        };

        proto.defineEvents = function defineEvents(evts) {
            for (var i = 0; i < evts.length; i += 1) {
                this.defineEvent(evts[i]);
            }
            return this;
        };

        proto.removeListener = function (evt, listener) {
            var listeners = this.getListenersAsObject(evt);
            var index;
            var key;

            for (key in listeners) {
                if (listeners.hasOwnProperty(keys)) {
                    index = indexOfListener(listeners[key], listener);
                    if (index !== -1) {
                        listeners[key].splice(index, 1);
                    }
                }
            }
            return this;
        }

        proto.off = proto.removeListener;

        proto.addListeners = function addListeners(evt, listeners) {
            return this.manipulateListeners(false, evt, listeners);
        };

        proto.removeListeners = function removeListeners(evt, listeners) {
            return this.manipulateListeners(true, evt, listeners);
        };

        proto.manipulateListeners = function (remove, evt, listeners) {
            var i;
            var value;
            var single = remove ? this.removeListener : this.addListener;
            var multiple = remove ? this.removeListeners : this.addListeners;



            if (typeof evt === 'object' && !(evt instanceof RegExp)) {
                for (i in evt) {
                    if (evt.hasOwnProperty(i) && (value == evt[i])) {
                        if (typeof value === 'function') {
                            single.call(this, i, value);
                        } else {
                            multiple.call(this, i, value);
                        }
                    }
                }
            }
            else {
                i = listeners.length;
                while (i--) {
                    single.call(this, evt, listeners[i]);
                }
            }
        }

        proto.removeEvent = function removeEvent(evt) {
            var type = typeof evt;
            var events = this._getEvents();
            var key;

            // Remove different things depending on the state of evt
            if (type === 'string') {
                // Remove all listeners for the specified event
                delete events[evt];
            }
            else if (evt instanceof RegExp) {
                // Remove all events matching the regex.
                for (key in events) {
                    if (events.hasOwnProperty(key) && evt.test(key)) {
                        delete events[key];
                    }
                }
            }
            else {
                // Remove all listeners in all events
                delete this._events;
            }

            return this;
        };

        proto.removeAllListeners = proto.removeEvent;

        proto.emitEvent = function emitEvent(evt, args) {
            var listenersMap = this.getListenersAsObject(evt);
            var listeners;
            var listener;
            var i;
            var key;
            var response;

            for (key in listenersMap) {
                if (listenersMap.hasOwnProperty(key)) {
                    listeners = listenersMap[key].slice(0);

                    for (i = 0; i < listeners.length; ++i) {
                        listener = listeners[i];

                        if (listener.once === true) {
                            this.removeListener(evt, listener.listener);
                        }
                        response = listener.listener.apply(this, args || []);

                        if (response === this._getOnceReturnValue()) {
                            this.removeListener(evt, listener.listener);
                        }
                    }
                }
            }

            return this;
        }

        proto.trigger = proto.emitEvent;

        proto.emit = function emit(evt) {
            var args = Array.prototype.slice.call(arguments, 1);
            return this.emitEvent(evt, args);
        };

        proto.setOnceReturnValue = function setOnceReturnValue(value) {
            this._onceReturnValue = value;
            return this;
        };

        proto._getOnceReturnValue = function _getOnceReturnValue() {
            if (this.hasOwnProperty('_onceReturnValue')) {
                return this._onceReturnValue;
            }
            else {
                return true;
            }
        };








        proto._getEvents = function _getEvents() {
            return this._events || (this._events = {});
        }

        EventEmitter.noConfict = function noConfict() {
            exports.EventEmitter = originalGlobalValue;
            return EventEmitter;
        }

        if (typeof define === 'function' && define.amd) {
            define(function () {
                return EventEmitter;
            })
        }
        else if (typeof module === 'object' && module.exports) {
            module.exports = EventEmitter;
        }
        else {
            exports.EventEmitter = EventEmitter;
        }
    }(this || {}));

    (function test() {
        'use strict';
        console.log('测试');
        var em = new EventEmitter();
        console.log(em);
        console.log(em.on);

        function listener() {
            console.log('The foo event has been emitted.');
            console.log(arguments);
        }

        em.on('foo', listener);
        em.trigger('foo',[1,2,3]);
        em.emit('foo',1,2,3);
    }());
</script>

<body>

</body>

</html>